#!/sbin/sh
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.
#
# Copyright 2016 Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org>
#

. /lib/svc/share/smf_include.sh
. /lib/svc/share/ipf_include.sh

PATH=${PATH}:/usr/sbin:/usr/lib/ipf
PIDFILE=/var/run/ipmon.pid
PFILCHECKED=no

zone=`smf_zonename`
ipfid=`/usr/sbin/modinfo 2>&1 | awk '/ipf/ { print $1 } ' - 2>/dev/null`
if [ -f $PIDFILE ] ; then
	pid=`cat $PIDFILE 2>/dev/null`
else
	pid=`pgrep -z $zone ipmon`
fi

logmsg()
{
	logger -p daemon.warning -t ipfilter "$1"
	echo "$1" >&2
}

load_ipf() {
	bad=0
	ipf -IFa
	ipf -6IFa

	for file in $IPFILOVRCONF $CONF_FILES $IPFILCONF; do
		if [ -r ${file} ]; then
			ipf -I -f ${file}
			if [ $? != 0 ]; then
				echo "$0: load of ${file} into alternate" \
				    "set failed"
				bad=1
			fi
		fi
	done

	for file in $IP6FILOVRCONF $CONF6_FILES $IP6FILCONF; do
		if [ -r ${file} ]; then
			ipf -6I -f ${file}
			if [ $? != 0 ]; then
				echo "$0: load of ${file} into alternate set failed"
				bad=1
			fi
		fi
	done

	if [ $bad -eq 0 ] ; then
		ipf -s -y
		return 0
	else
		echo "Not switching config due to load error."
		return 1
	fi
}


load_ipnat() {
	ipnat -CF
	for nfile in $NAT_FILES $IPNATCONF; do
		if [ -r ${nfile} ]; then
			ipnat -f ${nfile}
			if [ $? != 0 ]; then
				echo "$0: load of ${nfile} failed"
				return 1
			else
				ipf -y
			fi
		fi			
	done
}


load_ippool() {
	if [ -r ${IPPOOLCONF} ]; then
		ippool -F
		ippool -f ${IPPOOLCONF}
		if [ $? != 0 ]; then
			echo "$0: load of ${IPPOOLCONF} failed"
			return 1
		else
			return 0
		fi
	else
		return 0	
	fi
}

#
# Get current configuration version, fails if property doesn't exist.
# 
config_get_version()
{
        ver=`svcprop -p $FW_CONFIG_DEF_PG/version $SMF_FMRI 2>/dev/null`
        [ $? -ne 0 -o -z "$ver" ] && return 1

        echo "$ver"
}

#
# Version 1 configuration migration - if there's an existing ipf.conf file, set
# the default system-wide policy to "custom" and set the custom file value to
# "/etc/ipf/ipf.conf". Do this migration once and set the 'version' property
# to the current version value.
#
upgrade_config()
{
	old_ipfconf="/etc/ipf/ipf.conf"

	if [ -f ${old_ipfconf} ]; then
		grep '^[ \t]*[^# \t]' ${old_ipfconf} >/dev/null 2>&1
		if [ $? -eq 0 ]; then
			svccfg -s $SMF_FMRI setprop \
			    $FW_CONFIG_DEF_PG/$POLICY_PROP = astring: \
			    "custom" >/dev/null 2>&1
			svccfg -s $SMF_FMRI setprop \
			    $FW_CONFIG_DEF_PG/$CUSTOM_FILE_PROP = astring: \
			    "$old_ipfconf" >/dev/null 2>&1
		fi
	fi

	svccfg -s $SMF_FMRI setprop $FW_CONFIG_DEF_PG/version = count: \
	    "$CURRENT_VERSION" >/dev/null 2>&1
	svcadm refresh $SMF_FMRI >/dev/null 2>&1
}

configure_firewall()
{
	create_global_rules || exit $SMF_EXIT_ERR_CONFIG
	create_global_ovr_rules || exit $SMF_EXIT_ERR_CONFIG
	create_services_rules || exit $SMF_EXIT_ERR_CONFIG

	[ ! -f ${IPFILCONF} -a ! -f ${IPNATCONF} ] && exit 0
	ipf -E
	load_ippool || exit $SMF_EXIT_ERR_CONFIG
	load_ipf || exit $SMF_EXIT_ERR_CONFIG
	load_ipnat || exit $SMF_EXIT_ERR_CONFIG
}

#
# We handle configuration migration as well as a model change (transient to
# contract based service) in the start, stop, and refresh methods.
#
# Configuration migration is straightforward, the start method will do the
# upgrade if the repository version value is not the same as the version
# defined in ipf_include.sh However, there are two problems. First, ipfilter
# can start in parallel with manifest-import, thus the new configuration
# properties and service definition may not be available to the start method
# on the first reboot after an upgrade. Second, a transient to contract based
# model change isn't well supported for an online service.
#
# - If the start method finds the property missing (manifest-import hasn't
# completed), it will allow the still transient network/ipfilter to stay
# 'online' and wait for manifest-import. Once manifest-import completes, the
# refresh method will run svcadm restart if the version value is not
# up-to-date and the subsequent start method will perform the upgrade. 
#
# - Since the start method allows the service to stay online as a transient
# service (no contract), the svcadm restart invoked by refresh (described
# above) will result in a call to the stop method with no existing contract
# property. The ipfilter manifest cannot include contract/restarter token in
# its stop method definition since startd will fail to expand that token and
# place the service in maintenance. Thus, the stop method has to explicitly
# get the contract id before calling smf_kill_contract.
#
case "$1" in
	start)
		ver=`config_get_version`
		if [ $? -eq 1 ]; then
			echo "Warning: firewall properties are not available"
			exit $SMF_EXIT_OK
		fi

		[ "$ver" -ne "$CURRENT_VERSION" ] && upgrade_config

		configure_firewall

		/lib/svc/bin/svc.ipfd
		/usr/sbin/ipmon -Ds
		;;

	stop)
		ctid=`svcprop -p restarter/contract $SMF_FMRI`
		if [ -n "$ctid" ]; then
			smf_kill_contract $ctid TERM 1
		fi

		ipf -D
		[ "$zone" = "global" -a -n "$ipfid" ] && modunload -i $ipfid
		;;

	pause)
		ipfs -l
		ipfs -d /var/db/ipf -W
		ipf -D
		if [ -f $PIDFILE ] ; then
			if kill -0 $pid; then
				kill -TERM $pid
			else    
				cp /dev/null $PIDFILE
			fi
		fi      
		;;

	resume)
		ipf -E
		ipfs -R
		load_ippool
		load_ipf
		load_ipnat
		if [ -f $PIDFILE -a -n "$pid" ] ; then
			/usr/sbin/ipmon -Ds
		fi
		;;

	reload)
		ver=`config_get_version`
		if [ $? -eq 1 ]; then
			echo "Warning: firewall properties are not available"
			exit $SMF_EXIT_ERR_CONFIG
		fi

		if [ "$ver" -ne "$CURRENT_VERSION" ]; then
			svcadm restart $SMF_FMRI
			exit $SMF_EXIT_OK
		fi

		configure_firewall
		;;

	reipf)
		load_ipf
		;;

	reipnat)
		load_ipnat
		;;

	fw_update)
		#
		# The second argument is the fmri of the service to be updated.
		# If it's the network/ipfilter, we want to repopulate firewall
		# configuration for the entire system.
		#
		if [ "$2" = "$SMF_FMRI" ]; then
			configure_firewall
		else
			service_update $2 || exit 1
		fi
		;;

	*)
		echo "Usage: $0 \c" >&2
		echo "(start|stop|reload|reipf|reipnat|pause|resume)" >&2
		exit 1
		;;

esac
exit $SMF_EXIT_OK
